/* 
GeoGebra - Dynamic Mathematics for Schools
Copyright Markus Hohenwarter and GeoGebra Inc.,  http://www.geogebra.org

This file is part of GeoGebra.

This program is free software; you can redistribute it and/or modify it 
under the terms of the GNU General Public License as published by 
the Free Software Foundation.

 */

package geogebra.gui.toolbar;
import geogebra.Plain;
import geogebra.main.Application;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.SystemColor;
import java.awt.event.ActionEvent;
import java.io.Serializable;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Vector;

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;
import javax.swing.tree.*;

/**
 * Toolbar configuration panel.
 * 
 * @author Markus Hohenwarter, based on a dialog from geonext.de
 * 
 */
public class ToolbarConfigPanel extends javax.swing.JPanel
    implements
      java.awt.event.ActionListener,
      javax.swing.event.TreeExpansionListener {

  /**
   * 
   */
  private static final long serialVersionUID = 2431184695741961753L;

  private static final int SCROLL_PANEL_WIDTH = 300;
  private static final int SCROLL_PANEL_HEIGHT = 400;

  private final JButton insertButton;
  private final JButton moveUpButton;
  private final JButton moveDownButton;
  private final JButton deleteButton;
  private final JTree tree;
  private final JScrollPane configScrollPane;
  private final JScrollPane modeScrollPane;
  JScrollPane iconScrollPane;
  private final JPanel selectionPanel;
  private final JList toolList;
  private final DefaultListModel toolListModel;
  int selectedRow;
  private final Application app;

  protected ToolbarConfigPanel(Application app) {
    super();
    this.app = app;

    selectionPanel = new JPanel();
    selectionPanel.setLayout(new BorderLayout(5, 5));
    selectionPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
    setLayout(new BorderLayout(5, 5));

    tree = generateTree();

    toolListModel = new DefaultListModel();
    toolList = new JList(toolListModel);

    setToolBarString(app.getGuiManager().getToolBarDefinition());

    configScrollPane = new JScrollPane(tree);
    configScrollPane
        .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
    configScrollPane
        .setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
    // configScrollPane.setSize(150, 150);
    JPanel scrollSpacePanel = new JPanel();
    scrollSpacePanel.setLayout(new BorderLayout(0, 0));
    scrollSpacePanel.setBorder(new EmptyBorder(3, 5, 3, 5));
    scrollSpacePanel.add(configScrollPane, BorderLayout.CENTER); //
    JPanel scrollPanel = new JPanel();
    scrollPanel.setLayout(new BorderLayout(0, 0));
    scrollPanel.setBorder(new TitledBorder(geogebra.Menu.Toolbar));
    scrollPanel.add(scrollSpacePanel, BorderLayout.CENTER);

    scrollPanel.setPreferredSize(new Dimension(SCROLL_PANEL_WIDTH,
        SCROLL_PANEL_HEIGHT));
    //
    selectionPanel.add(scrollPanel, BorderLayout.WEST);
    //

    JPanel buttonPanel = new JPanel();
    buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.Y_AXIS));
    buttonPanel.add(Box.createVerticalGlue());

    insertButton = new JButton("< " + Plain.Insert);
    insertButton.addActionListener(this);
    insertButton.setAlignmentX(CENTER_ALIGNMENT);
    buttonPanel.add(insertButton);
    buttonPanel.add(Box.createVerticalStrut(10));

    deleteButton = new javax.swing.JButton(Plain.Remove + " >");
    deleteButton.addActionListener(this);
    deleteButton.setAlignmentX(CENTER_ALIGNMENT);
    buttonPanel.add(deleteButton);

    buttonPanel.add(Box.createVerticalGlue());
    selectionPanel.add(buttonPanel, BorderLayout.CENTER);

    JPanel upDownPanel = new JPanel();
    moveUpButton = new javax.swing.JButton("\u25b2 " + Plain.Up);
    moveUpButton.addActionListener(this);
    upDownPanel.add(moveUpButton);

    moveDownButton = new javax.swing.JButton("\u25bc " + Plain.Down);
    moveDownButton.addActionListener(this);
    upDownPanel.add(moveDownButton);

    scrollPanel.add(upDownPanel, BorderLayout.SOUTH);

    JPanel buttonAllPanel = new JPanel(new BorderLayout());
    buttonAllPanel.add(buttonPanel, BorderLayout.NORTH);
    JPanel tempPanel = new JPanel();
    tempPanel.setLayout(new BoxLayout(tempPanel, BoxLayout.Y_AXIS));
    tempPanel.add(Box.createRigidArea(new Dimension(10, 150)));
    tempPanel.add(buttonAllPanel);
    tempPanel.add(Box.createVerticalGlue());

    selectionPanel.add(tempPanel, BorderLayout.CENTER);
    JPanel modePanel = new JPanel();
    modePanel.setLayout(new BorderLayout(0, 0));
    modePanel.setBorder(new TitledBorder(geogebra.Menu.Tools));

    ListSelectionModel lsm = toolList.getSelectionModel();
    lsm.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
    toolList.setBackground(SystemColor.text);
    modeScrollPane = new JScrollPane(toolList);
    modeScrollPane
        .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
    modeScrollPane
        .setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
    toolList.setCellRenderer(new ModeCellRenderer(app));
    toolList.setSelectedIndex(0);
    //
    //
    JPanel modeSpacePanel = new JPanel();
    modeSpacePanel.setLayout(new BorderLayout(0, 0));
    modeSpacePanel.setBorder(new EmptyBorder(3, 5, 3, 5));
    modeSpacePanel.add("Center", modeScrollPane);

    modePanel.add("Center", modeSpacePanel);
    modePanel.setPreferredSize(new Dimension(SCROLL_PANEL_WIDTH,
        SCROLL_PANEL_HEIGHT));
    selectionPanel.add("East", modePanel);
    add("Center", selectionPanel);

    try {
      tree.setSelectionRow(1);
    } catch (Exception exc) {
      tree.setSelectionRow(0);
    }
  }

  /**
   * Handles remove, add and up, down buttons.
   */
  public void actionPerformed(ActionEvent e) {
    // get selected node in tree
    DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
    TreePath selPath = tree.getSelectionPath();
    if (selPath == null) {
      tree.setSelectionRow(0); // take root if nothing is selected
      selPath = tree.getSelectionPath();
    }
    DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
    DefaultMutableTreeNode selNode = (DefaultMutableTreeNode) selPath
        .getLastPathComponent();
    // remember row number
    int selRow = tree.getRowForPath(selPath);

    DefaultMutableTreeNode parentNode;
    if (selNode == root)
      parentNode = selNode;
    else
      parentNode = (DefaultMutableTreeNode) selNode.getParent();
    int childIndex = parentNode.getIndex(selNode);

    Object src = e.getSource();

    // DELETE
    if (src == deleteButton) {
      if (selRow > 0) { // not root
        // delete node unless move node
        Object userOb = selNode.getUserObject();
        if (userOb == null)
          userOb = ((DefaultMutableTreeNode) selNode.getFirstChild())
              .getUserObject();
        if (userOb instanceof Integer && ((Integer) userOb).intValue() == 0)
          return;

        // not move mode: delete node
        model.removeNodeFromParent(selNode);
        // remove empty menu too
        if (parentNode.getChildCount() == 0)
          if (!parentNode.isRoot()) {
            model.removeNodeFromParent(parentNode);
            selRow--;
          }

        toolListModel.addElement(userOb);
        sortToolList();

        // select node at same row or above
        if (selRow >= tree.getRowCount())
          selRow--;
        tree.setSelectionRow(selRow);
      }
    }
    // INSERT
    else if (src == insertButton) {
      childIndex++;

      boolean didInsert = false;
      Object[] tools = toolList.getSelectedValues();
      for (Object tool : tools) {
        // check if too is already there
        Integer modeInt = (Integer) tool;
        if (modeInt.intValue() > -1 && containsTool(root, (Integer) tool))
          continue;

        DefaultMutableTreeNode newNode;
        if (parentNode == root && modeInt.intValue() > -1) {
          // parent is root: create new submenu
          newNode = new DefaultMutableTreeNode();
          newNode.add(new DefaultMutableTreeNode(modeInt));
        } else
          // parent is submenu
          newNode = new DefaultMutableTreeNode(modeInt);
        model.insertNodeInto(newNode, parentNode, childIndex++);
        didInsert = true;

        // remove node from list of unused tools if the node is not a separator
        if (modeInt.intValue() > -1)
          toolListModel.removeElement(modeInt);
      }

      if (didInsert) {
        // make sure that root is expanded
        tree.expandPath(new TreePath(model.getRoot()));

        // select first inserted node
        tree.setSelectionRow(++selRow);
        tree.scrollRowToVisible(selRow);
        configScrollPane.getHorizontalScrollBar().setValue(0); // scroll to left

        // sort tool list
        sortToolList();
      }
    }

    // UP
    else if (src == moveUpButton) {
      if (selNode == root)
        return;

      if (parentNode.getChildBefore(selNode) != null) {
        model.removeNodeFromParent(selNode);
        model.insertNodeInto(selNode, parentNode, --childIndex);
        tree.setSelectionRow(--selRow);
      }
    }

    // DOWN
    else if (src == moveDownButton) {
      if (selNode == root)
        return;

      if (parentNode.getChildAfter(selNode) != null) {
        model.removeNodeFromParent(selNode);
        model.insertNodeInto(selNode, parentNode, ++childIndex);
        tree.setSelectionRow(++selRow);
      }
    }
  }

  private void collapseAllRows() {
    int z = tree.getRowCount();
    for (int i = z; i > 0; i--)
      tree.collapseRow(i);
  }

  private boolean containsTool(DefaultMutableTreeNode node, Integer mode) {
    // compare modes
    Object ob = node.getUserObject();
    if (ob != null && mode.compareTo((Integer) ob) == 0)
      return true;

    if (node.getChildCount() >= 0)
      for (Enumeration<?> e = node.children(); e.hasMoreElements();) {
        DefaultMutableTreeNode n = (DefaultMutableTreeNode) e.nextElement();
        if (containsTool(n, mode))
          return true;
      }
    return false;
  }

  /**
	 * 
	 */
  private DefaultMutableTreeNode generateRootNode(
      Vector<Serializable> toolbarModes) {
    DefaultMutableTreeNode node = new DefaultMutableTreeNode();

    for (int i = 0; i < toolbarModes.size(); i++) {
      Object ob = toolbarModes.get(i);
      if (ob instanceof Vector<?>) {
        Vector<Integer> menu = (Vector<Integer>) ob;
        DefaultMutableTreeNode sub = new DefaultMutableTreeNode();
        for (int j = 0; j < menu.size(); j++)
          sub.add(new DefaultMutableTreeNode(menu.get(j)));
        node.add(sub);
      } else
        node.add(new DefaultMutableTreeNode(ob));
    }
    return node;
  }

  /**
	 * 
	 */
  private Vector<Integer> generateToolsVector(String toolbarDefinition) {
    Vector<Integer> vector = new Vector<Integer>();
    // separator
    vector.add(MyToolbar.TOOLBAR_SEPARATOR);

    // get default toolbar as nested vectors
    Vector<Serializable> defTools = MyToolbar
        .createToolBarVec(toolbarDefinition);
    for (int i = 0; i < defTools.size(); i++) {
      Object element = defTools.get(i);

      if (element instanceof Vector<?>) {
        Vector<Integer> menu = (Vector<Integer>) element;
        for (int j = 0; j < menu.size(); j++) {
          Integer modeInt = menu.get(j);
          int mode = modeInt.intValue();
          if (mode != -1)
            vector.add(modeInt);
        }
      } else {
        Integer modeInt = (Integer) element;
        int mode = modeInt.intValue();
        if (mode != -1)
          vector.add(modeInt);
      }
    }
    return vector;
  }
  /**
	 *
	 */
  private JTree generateTree() {
    final JTree jTree = new JTree() {
      /**
       * 
       */
      private static final long serialVersionUID = 1L;

      @Override
      protected void setExpandedState(TreePath path, boolean state) {
        // Ignore all collapse requests of root
        if (path != getPathForRow(0))
          super.setExpandedState(path, state);
      }
    };

    jTree.setCellRenderer(new ModeCellRenderer(app));
    jTree.getSelectionModel().setSelectionMode(
        TreeSelectionModel.SINGLE_TREE_SELECTION);
    jTree.putClientProperty("JTree.lineStyle", "Angled");
    jTree.addTreeExpansionListener(this);

    return jTree;
  }
  /**
   * Returns the custom toolbar created with this panel as a String. Separator
   * ("||" between menus, "," in menu), New menu starts with "|"
   */
  public String getToolBarString() {
    StringBuffer sb = new StringBuffer();

    DefaultMutableTreeNode root = (DefaultMutableTreeNode) tree.getModel()
        .getRoot();
    for (int i = 0; i < root.getChildCount(); i++) {
      DefaultMutableTreeNode menu = (DefaultMutableTreeNode) root.getChildAt(i);

      if (menu.getChildCount() == 0)
        sb.append("|| ");
      else if (i > 0 && !sb.toString().endsWith("|| ")) // new menu
        sb.append("| ");

      for (int j = 0; j < menu.getChildCount(); j++) {
        DefaultMutableTreeNode node = (DefaultMutableTreeNode) menu
            .getChildAt(j);
        int mode = ((Integer) node.getUserObject()).intValue();

        if (mode < 0) // separator
          sb.append(", ");
        else { // mode number
          sb.append(mode);
          sb.append(" ");
        }
      }
    }

    return sb.toString().trim();
  }
  /**
   * Inits the toolbar tree in this panel to show the given toolbar definition
   * string.
   */
  public void setToolBarString(String toolbarDefinition) {
    // create new tree model
    Vector<Serializable> toolVec = MyToolbar
        .createToolBarVec(toolbarDefinition);
    DefaultTreeModel model = new DefaultTreeModel(generateRootNode(toolVec));
    tree.setModel(model);
    collapseAllRows();
    tree.setRowHeight(-1);

    Vector<Integer> allTools = generateToolsVector(app.getGuiManager()
        .getDefaultToolbarString());
    Vector<Integer> usedTools = generateToolsVector(toolbarDefinition);

    toolListModel.clear();
    toolListModel.addElement(MyToolbar.TOOLBAR_SEPARATOR); // always display the
    // separator in the
    // tools list

    for (Iterator<Integer> iter = allTools.iterator(); iter.hasNext();) {
      Object next = iter.next();

      if (!usedTools.contains(next))
        toolListModel.addElement(next);
    }
  }

  /**
   * Add an item to the tool list
   * 
   * TODO Rename method TODO Use this method to insert new items into the model
   * TODO Use the default toolbar vector to keep the standard sorting
   * 
   * @author Florian Sonner
   * @version 2008-10-22
   */
  private void sortToolList() {
    /*
     * int numItems = toolListModel.getSize();
     * 
     * if (numItems < 2) return;
     * 
     * // copy list data into an array Integer[] a = new Integer[numItems]; for
     * (int i = 0; i < numItems; ++i) { a[i] = (Integer)
     * toolListModel.getElementAt(i); }
     * 
     * // sort array.. Arrays.sort(a);
     * 
     * // copy the sorted array back into the model for (int i = 0; i <
     * numItems; ++i) { toolListModel.setElementAt(a[i], i); }
     */
  }

  /**
	 * 
	 */
  public void treeCollapsed(javax.swing.event.TreeExpansionEvent event) {
  }
  /**
	 * 
	 */
  public void treeExpanded(javax.swing.event.TreeExpansionEvent event) {
    /*
     * tabbed.invalidate(); tabbed.validateTree();
     */
  }

  /**
	 * 
	 */
  public void valueChanged(javax.swing.event.ListSelectionEvent e) {
  }

}